package com.ct.lotterysystem2.service.impl;

import com.ct.lotterysystem2.common.errorcode.ServiceErrorCodeConstants;
import com.ct.lotterysystem2.common.exception.ServiceException;
import com.ct.lotterysystem2.common.utils.JacksonUtil;
import com.ct.lotterysystem2.common.utils.RedisUtil;
import com.ct.lotterysystem2.controller.param.CreateActivityParam;
import com.ct.lotterysystem2.controller.param.CreatePrizeByActivityParam;
import com.ct.lotterysystem2.controller.param.CreateUserByActivityParam;
import com.ct.lotterysystem2.controller.param.PageParam;
import com.ct.lotterysystem2.dao.dataobject.ActivityDO;
import com.ct.lotterysystem2.dao.dataobject.ActivityPrizeDO;
import com.ct.lotterysystem2.dao.dataobject.ActivityUserDO;
import com.ct.lotterysystem2.dao.dataobject.PrizeDO;
import com.ct.lotterysystem2.dao.mapper.ActivityMapper;
import com.ct.lotterysystem2.dao.mapper.ActivityPrizeMapper;
import com.ct.lotterysystem2.dao.mapper.ActivityUserMapper;
import com.ct.lotterysystem2.dao.mapper.PrizeMapper;
import com.ct.lotterysystem2.service.ActivityService;
import com.ct.lotterysystem2.service.dto.ActivityDTO;
import com.ct.lotterysystem2.service.dto.ActivityDetailDTO;
import com.ct.lotterysystem2.service.dto.CreateActivityDTO;
import com.ct.lotterysystem2.service.dto.PageListDTO;
import com.ct.lotterysystem2.service.enums.ActivityPrizeTiersEnum;
import com.ct.lotterysystem2.service.enums.ActivityStatusEnum;
import com.ct.lotterysystem2.service.enums.ActivityUserStatusEnum;
import com.ct.lotterysystem2.service.enums.ActivityPrizeStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ActivityServiceImpl implements ActivityService {

    /**
     * 活动缓存前缀, 为了区分前缀
     */
    private final String ACTIVITY_PREFIX = "ACTIVITY_";

    /**
     * 活动缓存过期时间
     */
    private final Long ACTIVITY_TIME_OUT = 60 * 60 * 24 * 3L;

    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private ActivityUserMapper activityUserMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private RedisUtil redisUtil;

    @Override
    public CreateActivityDTO createActivity(CreateActivityParam param) {
        ActivityDO activityDO = new ActivityDO();
        activityDO.setActivityName(param.getActivityName());
        activityDO.setDescription(param.getDescription());
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name());
        activityMapper.insert(activityDO);

        // 保存活动相关人员
        List<CreateUserByActivityParam> userParams = param.getActivityUserList();
        List<ActivityUserDO> activityUserDOList = userParams.stream()
                .map(userParam -> {
                    ActivityUserDO result = new ActivityUserDO();
                    result.setActivityId(activityDO.getId());
                    result.setUserId(userParam.getUserId());
                    result.setUserName(userParam.getUserName());
                    result.setStatus(ActivityUserStatusEnum.INIT.name());
                    return result;
                }).collect(Collectors.toList());
        activityUserMapper.batchInsert(activityUserDOList);

        // 保存活动相关奖品
        List<CreatePrizeByActivityParam> prizeParams = param.getActivityPrizeList();
        List<ActivityPrizeDO> activityPrizeDOList = prizeParams.stream()
                .map(prizeParam -> {
                    ActivityPrizeDO result = new ActivityPrizeDO();
                    result.setActivityId(activityDO.getId());
                    result.setPrizeId(prizeParam.getPrizeId());
                    result.setPrizeAmount(prizeParam.getPrizeAmount());
                    result.setPrizeTiers(prizeParam.getPrizeTiers());
                    result.setStatus(ActivityPrizeStatusEnum.INIT.name());
                    return result;
                }).collect(Collectors.toList());
        activityPrizeMapper.batchInsert(activityPrizeDOList);

        // 整合完整的活动信息, 存放redis
        // activityId : ActivityDetailInfoDTO : 活动 + 奖品 + 人员

        // 先获取奖品基本属性列表
        // 获取需要查询的奖品id
        List<Long> prizeIds = param.getActivityPrizeList().stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());

        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeIds);

        ActivityDetailDTO detailDTO = convetToActivityDetailDTO(activityDO, activityUserDOList,
                prizeDOList, activityPrizeDOList);

        cacheActivity(detailDTO);

        CreateActivityDTO createActivityDTO = new CreateActivityDTO();
        createActivityDTO.setActivityId(activityDO.getId());

        return createActivityDTO;
    }

    @Override
    public PageListDTO<ActivityDTO> findActivityList(PageParam param) {

        int count = activityMapper.count();

        List<ActivityDO> activityDOList = activityMapper.selectActivityList(param.offset(), param.getPageSize());

        List<ActivityDTO> activityDTOList = activityDOList.stream()
                .map(activityDO -> {
                    ActivityDTO activityDTO = new ActivityDTO();
                    activityDTO.setActivityId(activityDO.getId());
                    activityDTO.setActivityName(activityDO.getActivityName());
                    activityDTO.setDescription(activityDO.getDescription());
                    activityDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));
                    return activityDTO;
                }).collect(Collectors.toList());

        return new PageListDTO<>(count, activityDTOList);
    }

    private void cacheActivity(ActivityDetailDTO detailDTO) {
        if (null == detailDTO ||
                null == detailDTO.getActivityId()) {
            log.warn("要缓存的活动信息不存在!");
            return;
        }

        try {
            redisUtil.set(ACTIVITY_PREFIX + detailDTO.getActivityId(),
                    JacksonUtil.writerValueAsString(detailDTO),
                    ACTIVITY_TIME_OUT);
        } catch (Exception e) {
            log.error("缓存活动异常, ActivityDetailDTO={}",
                    JacksonUtil.writerValueAsString(detailDTO));
        }
    }

    private ActivityDetailDTO convetToActivityDetailDTO(ActivityDO activityDO,
                                                        List<ActivityUserDO> activityUserDOList,
                                                        List<PrizeDO> prizeDOList,
                                                        List<ActivityPrizeDO> activityPrizeDOList) {
        ActivityDetailDTO detailDTO = new ActivityDetailDTO();
        detailDTO.setActivityId(activityDO.getId());
        detailDTO.setActivityName(activityDO.getActivityName());
        detailDTO.setDesc(activityDO.getDescription());
        detailDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));

        detailDTO.setPrizeDTOList(activityPrizeDOList.stream()
                .map(activityPrizeDO -> {
                    ActivityDetailDTO.PrizeDTO prizeDTO = new ActivityDetailDTO.PrizeDTO();
                    prizeDTO.setPrizeId(activityPrizeDO.getPrizeId());
                    Optional<PrizeDO> optionalPrizeDO = prizeDOList.stream()
                            .filter(prizeDO -> prizeDO.getId().equals(activityPrizeDO.getPrizeId()))
                            .findFirst();
                    optionalPrizeDO.ifPresent(prizeDO -> {
                        prizeDTO.setName(prizeDO.getName());
                        prizeDTO.setImageUrl(prizeDO.getImageUrl());
                        prizeDTO.setPrice(prizeDO.getPrice());
                        prizeDTO.setDescription(prizeDO.getDescription());
                    });
                    prizeDTO.setTiers(ActivityPrizeTiersEnum.forName(activityPrizeDO.getPrizeTiers()));
                    prizeDTO.setPrizeAmount(activityPrizeDO.getPrizeAmount());
                    prizeDTO.setStatus(ActivityPrizeStatusEnum.forName(activityPrizeDO.getStatus()));
                    return prizeDTO;
                }).collect(Collectors.toList())
        );

        detailDTO.setUserDTOList(activityUserDOList.stream()
                .map(activityUserDO -> {
                    ActivityDetailDTO.UserDTO userDTO = new ActivityDetailDTO.UserDTO();
                    userDTO.setUserId(activityUserDO.getUserId());
                    userDTO.setUserName(activityUserDO.getUserName());
                    userDTO.setStatus(ActivityUserStatusEnum.forName(activityUserDO.getStatus()));
                    return userDTO;
                }).collect(Collectors.toList())
        );

        return detailDTO;
    }
}
